package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.NestedQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.*;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;
    private Suggester.Builder f;

    /**
     * 改造使用并行实现专辑上架，多线程，需要考虑多个方法之间的关系，是否有因果关系
     * @param albumId
     */
    public void upperAlbum(Long albumId) {
        //创建一个新的AlbumInfoIndex对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        //根据专辑id获取专辑信息
        CompletableFuture<AlbumInfo> completableFuture1 =
                //supplyAsync，表示创建一个线程来处理里面的操作，有返回值
                CompletableFuture.supplyAsync(() -> {
                    //调用远程方法，获取到数据
                    Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
                    AlbumInfo albumInfo = albumInfoResult.getData();
                    Assert.notNull(albumInfo, "专辑为空");
                    //封装到albumInfoIndex
                    BeanUtils.copyProperties(albumInfo, albumInfoIndex);
                    return albumInfo;
                }, threadPoolExecutor);

        //获取分类数据
        //专辑获取之后执行
        CompletableFuture<Void> completableFuture2 =
                completableFuture1.thenAcceptAsync(albumInfo -> {
                    //获取三级分类id
                    Long category3Id = albumInfo.getCategory3Id();
                    Result<BaseCategoryView> categoryViewResult = categoryFeignClient.getCategoryView(category3Id);
                    BaseCategoryView baseCategoryView = categoryViewResult.getData();
                    Assert.notNull(baseCategoryView, "分类数据为空");

                    //封装三个分类id到albumInfoIndex
                    Long category1Id = baseCategoryView.getCategory1Id();
                    Long category2Id = baseCategoryView.getCategory2Id();
                    albumInfoIndex.setCategory1Id(category1Id);
                    albumInfoIndex.setCategory2Id(category2Id);
                    albumInfoIndex.setCategory3Id(category3Id);
                }, threadPoolExecutor);

        //获取专辑对应标签数据
        CompletableFuture<Void> completableFuture3 = CompletableFuture.runAsync(() -> {
            Result<List<AlbumAttributeValue>> albumAttributeValueResult = albumInfoFeignClient.findAlbumAttributeValue(albumId);
            List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueResult.getData();
            //封装 albumInfoIndex
            // List<AlbumAttributeValue> -->  List<AttributeValueIndex>
            if (!CollectionUtils.isEmpty(albumAttributeValueList)) {
                List<AttributeValueIndex> attributeValueIndexList =
                        albumAttributeValueList.stream().map(albumAttributeValue -> {
                            AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                            BeanUtils.copyProperties(albumAttributeValue, attributeValueIndex);
                            return attributeValueIndex;
                        }).collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            }

        }, threadPoolExecutor);

        //获取用户信息
        CompletableFuture<Void> completableFuture4 = completableFuture1.thenAcceptAsync(albumInfo -> {
            Long userId = albumInfo.getUserId();
            Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            //封装 albumInfoIndex
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        }, threadPoolExecutor);

        //  赋值：初始化统计信息
        int playStatNum = new Random().nextInt(100000);
        int subscribeStatNum = new Random().nextInt(100000000);
        int buyStatNum = new Random().nextInt(10000000);
        int commentStatNum = new Random().nextInt(1000000000);
        albumInfoIndex.setPlayStatNum(playStatNum);
        albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
        albumInfoIndex.setBuyStatNum(buyStatNum);
        albumInfoIndex.setCommentStatNum(commentStatNum);

        //等待所有任务都完成，汇总
        CompletableFuture.allOf(
                completableFuture1,
                completableFuture2,
                completableFuture3,
                completableFuture4
        ).join();

        //调用方法添加es
        albumInfoIndexRepository.save(albumInfoIndex);

        //添加数据到suggestinfo索引库，后面实现自动补全
        SuggestIndex suggestIndex = new SuggestIndex();
        //id
        suggestIndex.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        //专辑标题
        suggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        //标题中文
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        //中文的完整汉语拼音，比如xiaoshuo
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumTitle())}));
        //中文的汉语拼音首字母，比如xs
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumTitle())}));

        this.suggestIndexRepository.save(suggestIndex);
    }

    /**
     * 下架专辑
     * @param albumId
     */
    @Override
    public void lowerAlbum(Long albumId) {
        //直接从Elasticsearch中根据专辑id删除专辑即可
        albumInfoIndexRepository.deleteById(albumId);
    }

    /**
     * 专辑检索的接口（搜索栏）
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) throws Exception {
        //  调用方法，构建dsl语句,将构建dsl语句的方法提取出来，防止代码过长， 降低代码可读性
        SearchRequest request = this.buildQueryDsl(albumIndexQuery);
        //调用 elasticsearchClient里面search执行检索
        SearchResponse<AlbumInfoIndex> response =
                elasticsearchClient.search(request, AlbumInfoIndex.class);
        //把最终结果封装到AlbumSearchResponseVo，一层一层读取，然后挨个赋值
        AlbumSearchResponseVo searchResponseVo = this.parseSearchResult(response);
        //设置当前页
        searchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        //每页记录数
        searchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        //总页数
        // 总页数 =（总记录数 + 每页显示记录数 -1）/ 每页显示记录数
        long totalPages = (searchResponseVo.getTotal() + albumIndexQuery.getPageSize() - 1) / albumIndexQuery.getPageSize();
        searchResponseVo.setTotalPages(totalPages);
        //返回vo对象
        return searchResponseVo;
    }

    /**
     * 首页接口
     * @param category1Id 一级分类ID
     * @return
     * @throws Exception
     */
    @Override
    public List<Map<String, Object>> channel(Long category1Id) throws Exception {
        //1 远程调用：根据一级分类id 获取下面前7个置顶的三级分类id
        //前7个三级分类id集合形式
        Result<List<BaseCategory3>> topBaseCategory3Result = categoryFeignClient.findTopBaseCategory3(category1Id);
        //集合存在data里面，从data中获得前7个三级分类的集合
        List<BaseCategory3> baseCategory3List = topBaseCategory3Result.getData();

        //List<BaseCategory3>  -- Map<三级分类id，三级分类对象>
        //通过Map类型方便存也方便取的特点，从集合中将三级分类对象取出来,key是三级分类ID，value是三级分类对象信息
        Map<Long, BaseCategory3> category3Map = baseCategory3List.stream().collect(
                Collectors.toMap(BaseCategory3::getId,
                        BaseCategory3 -> BaseCategory3));

        //获取到三级分类Id 集合
        //从前7个三级分类id集合中获取到三级分类的ID
        List<Long> category3IdList = baseCategory3List.stream()
                .map(BaseCategory3::getId).collect(Collectors.toList());

        // List<Long> -- List<FieldValue>
        //将long类型的集合转成FieldValue类型集合，通过stream流的形式
        //FieldValue.of(id) 是 Elasticsearch 客户端 API 提供的静态方法，用于创建 FieldValue 实例
        List<FieldValue> idValueList = category3IdList.stream()
                .map(id -> FieldValue.of(id)).collect(Collectors.toList());

        //2 编写DSL语句查询es
        SearchRequest.Builder requestBuilder = new SearchRequest.Builder();

        requestBuilder.index("albuminfo").query(
                q -> q.terms(
                        f -> f.field("category3Id")
                                .terms(
                                        new TermsQueryField.Builder().value(idValueList).build())
                ));

        requestBuilder.aggregations("groupByCategory3IdAgg",
                a -> a.terms(f -> f.field("category3Id").size(10))
                        .aggregations("topTenHotScoreAgg",
                                a1 -> a1.topHits(
                                        s -> s.size(6)
                                                .sort(o -> o.field(o1 -> o1.field("hotScore")
                                                        .order(SortOrder.Desc)))))
        );
        //获取返回结果
        SearchResponse<AlbumInfoIndex> response =
                elasticsearchClient.search(requestBuilder.build(), AlbumInfoIndex.class);

        //3 从es查询返回结果获取需要数据，封装到map集合
        //  声明集合
        List<Map<String, Object>> result = new ArrayList<>();
        //  从聚合中获取数据
        Aggregate groupByCategory3IdAgg =
                response.aggregations().get("groupByCategory3IdAgg");
        groupByCategory3IdAgg.lterms().buckets().array().forEach(item -> {
            //  创建集合数据
            List<AlbumInfoIndex> albumInfoIndexList = new ArrayList<>();
            //  获取三级分类Id 对象
            long category3Id = item.key();
            //  获取要置顶的集合数据
            Aggregate topTenHotScoreAgg = item.aggregations().get("topTenHotScoreAgg");
            //  循环遍历获取聚合中的数据
            topTenHotScoreAgg.topHits().hits().hits().forEach(hit -> {
                //  获取到source 的json 字符串数据
                String json = hit.source().toString();
                //  将json 字符串转换为AlbumInfoIndex 对象
                AlbumInfoIndex albumInfoIndex = JSON.parseObject(json, AlbumInfoIndex.class);
                //  将对象添加到集合中
                albumInfoIndexList.add(albumInfoIndex);
            });
            //  声明一个map 集合数据
            Map<String, Object> map = new HashMap<>();
            //  存储根据三级分类Id要找到的三级分类
            map.put("baseCategory3", category3Map.get(category3Id));
            //  存储所有的专辑集合数据
            map.put("list", albumInfoIndexList);
            //  将map 添加到集合中
            result.add(map);
        });

        return result;
    }

    /**
     * 自动补全功能
     * @param keyword
     * @return
     */
    @Override
    public List<String> completeSuggest(String keyword) throws Exception {
        //思路：编写DSL语句
        //先创建一个对象
        SearchRequest.Builder requestBuilder = new SearchRequest.Builder();
        //对应DSL语句实现在ES中的查询
        requestBuilder.suggest(
                s -> s.suggesters(
                                "suggestionKeyword", f -> f.prefix(keyword)
                                        .completion(c -> c.field("keyword")
                                                .skipDuplicates(true)
                                                .size(10)
                                                .fuzzy(x -> x.fuzziness("auto"))))
                        .suggesters(
                                "suggestionkeywordPinyin", f -> f.prefix(keyword)
                                        .completion(
                                                m -> m.field("keywordPinyin")
                                                        .skipDuplicates(true)
                                                        .size(10)
                                                        .fuzzy(z -> z.fuzziness("auto"))
                                        )
                        )
                        .suggesters("suggestionkeywordSequence",f->f.prefix(keyword).completion(
                                c->c.field("keywordSequence").skipDuplicates(true).size(10)
                                        .fuzzy(z->z.fuzziness("auto"))
                        ))
        );

        //最后调用搜索，使用builder.build工厂模式
        SearchResponse<SuggestIndex> response =
                elasticsearchClient.search(requestBuilder.build(), SuggestIndex.class);

        //对结果进行处理
        HashSet<String> titleSet = new HashSet<>();
        titleSet.addAll(this.parseResultData(response,"suggestionKeyword"));
        titleSet.addAll(this.parseResultData(response,"suggestionkeywordPinyin"));
        titleSet.addAll(this.parseResultData(response,"suggestionkeywordSequence"));

        //如果查询Completion没有达到10条匹配，根据title做普通匹配，补全，如果实在无法补全，就直接返回
        if(titleSet.size()<10) {
            SearchResponse<SuggestIndex> searchResponse =
                    elasticsearchClient.search(s -> s.index("suggestinfo")
                                    .query(f -> f.match(m -> m.field("title").query(keyword)))
                            , SuggestIndex.class);
            //  从查询结果集中获取数据
            for (Hit<SuggestIndex> hit : response.hits().hits()) {
                //  获取数据结果
                SuggestIndex suggestIndex = hit.source();
                //  获取titile
                titleSet.add(suggestIndex.getTitle());
                //  判断当前这个结合的长度.
                if (titleSet.size()==10){
                    break;
                }
            }
        }
        return new ArrayList<>(titleSet);
    }

    /**
     * 处理结果
     * @param response
     * @param suggestionKeyword
     * @return
     */
    private List<String> parseResultData(SearchResponse<SuggestIndex> response,
                                         String suggestionKeyword) {
        List<String> suggestList = new ArrayList<>();
        Map<String, List<Suggestion<SuggestIndex>>> map = response.suggest();
        //根据名称获取值
        List<Suggestion<SuggestIndex>> suggestions = map.get(suggestionKeyword);
        suggestions.forEach(item -> {
            CompletionSuggest<SuggestIndex> completionSuggest =  item.completion();
            completionSuggest.options().forEach(it -> {
                SuggestIndex suggestIndex = it.source();
                suggestList.add(suggestIndex.getTitle());
            });
        });
        //  返回集合列表
        return suggestList;
    }

    /**
     * 构建DSL语句
     * @param albumIndexQuery 专辑检索的对象，里面有关键字字段
     * @return
     */
    private SearchRequest buildQueryDsl(AlbumIndexQuery albumIndexQuery) {
        //创建SearchRequestBuilder,这里ES创建对象都是使用builder.build实现的
        SearchRequest.Builder requestBuilder = new SearchRequest.Builder();
        //requestBuilder构建dsl语句
        //1、创建BoolQuery对象,思路是将整个的语句划分成多个部分，在最终编写完成之后，再通过调用build()方法来实际创建
        BoolQuery.Builder boolQuery = new BoolQuery.Builder();
        //获取到关键词
        //bool - should - match - albumTitle
        String keyword = albumIndexQuery.getKeyword();
        //非空判断
        if (!StringUtils.isEmpty(keyword)) {
            boolQuery.should(m -> m.match(f -> f.field("albumTitle").query(keyword)));
            boolQuery.should(m -> m.match(f -> f.field("albumIntro").query(keyword)));

            //高亮显示
            requestBuilder.highlight(h -> h.fields("albumTitle",
                    f -> f.preTags("<span style=color:red>").postTags("</span>")));
        }

        //进行过滤，值必须等于三级分类Id
        //bool - filter - term - category3Id
        //先获取到三级分类ID
        Long category3Id = albumIndexQuery.getCategory3Id();
        //非空判断
        if (!StringUtils.isEmpty(category3Id)) {
            boolQuery.filter(t -> t.term(c -> c.field("category3Id").value(category3Id)));
        }

        //  一级分类Id,后面可能会用到
        Long category1Id = albumIndexQuery.getCategory1Id();
        if (!StringUtils.isEmpty(category1Id)) {
            boolQuery.filter(f -> f.term(s -> s.field("category1Id").value(category1Id)));
        }
        //  二级分类Id
        Long category2Id = albumIndexQuery.getCategory2Id();
        if (!StringUtils.isEmpty(category2Id)) {
            boolQuery.filter(f -> f.term(s -> s.field("category2Id").value(category2Id)));
        }

        //根据属性ID和属性值ID进行过滤
        //先获取属性ID和属性值ID
        List<String> attributeList = albumIndexQuery.getAttributeList();
        //非空判断
        if (!CollectionUtils.isEmpty(attributeList)) {
            //遍历集合,
            for (String attribute : attributeList) {
                //它的属性ID和属性值ID的存储格式是A:B的方式，所以我们需要先通过split方法将他们分开
                String[] split = attribute.split(":");
                //校验
                if (null != split && split.length == 2) {
                    //
                    NestedQuery nestedQuery = NestedQuery.of(
                            f -> f.path("attributeValueIndexList")
                                    .query(b -> b.bool(m -> m.must(t -> t.term(
                                                    f1 -> f1.field("attributeValueIndexList.attributeId").value(split[0])))
                                            .must(t -> t.term(f2 -> f2.field("attributeValueIndexList.valueId").value(split[1])))))
                    );
                    //这是将后面的对于属性ID和属性值ID的比较提取成一个变量，方便读，实际结构和语句相同
                    boolQuery.filter(f -> f.nested(nestedQuery));
                }
            }
        }

        //排序（分为3种情况）
        //综合排序  播放量  发布时间
        //1：ads   2:desc  3:desc
        //先获取排序规则
        String order = albumIndexQuery.getOrder();
        //非空判断
        if (!StringUtils.isEmpty(order)) {
            //根据:切分
            String[] split = order.split(":");
            //定义一个排序字段
            String orderField = "";
            //定义排序规则
            String sort = "";
            //对划分后的数组校验
            if (split != null && split.length == 2) {
                //需要根据split[0]的数字获取对应的排序字段
                switch (split[0]) {
                    case "1":
                        orderField = "hotScore";
                        break;
                    case "2":
                        orderField = "playStatNum";
                        break;
                    case "3":
                        orderField = "createTime";
                        break;
                }
                sort = split[1];
            }

            //因为dsl语句中变量必须被final修饰，也就是变量不可以被修改
            String finalSort = sort;
            String finalOrderField = orderField;
            //排序dsl语句,这里是将对应的排序规则和asc进行比较，如果相同，说明是正序，否则是倒序排序
            requestBuilder.sort(a -> a.field(b -> b.field(finalOrderField)
                    .order("asc".equals(finalSort) ? SortOrder.Asc : SortOrder.Desc)));
        } else {
            //默认排序方式
            //默认排序规则 _score
            requestBuilder.sort(f -> f.field(o -> o.field("_score").order(SortOrder.Desc)));
        }

        //字段选择，在hits - hits - source字段下，存放着数据
        requestBuilder.source(s -> s.filter(f -> f.excludes("attributeValueIndexList")));

        //分页：计算公式:当前页的起始位置：（当前页的页数-1）* 每页数据条数
        Integer from = (albumIndexQuery.getPageNo() - 1) * albumIndexQuery.getPageSize();
        //设置分页的from和size
        requestBuilder.from(from);
        requestBuilder.size(albumIndexQuery.getPageSize());

        //albuminfo - query - match - albumTitle
        requestBuilder.index("albuminfo").query(m -> m.bool(boolQuery.build()));

        //requestBuilder的build返回SearchRequest
        SearchRequest searchRequest = requestBuilder.build();
        //将我们和DSL语句对应实现的代码输出到控制台
        System.out.println("dsl" + searchRequest.toString());

        return searchRequest;
    }

    /**
     * 把es查询返回SearchResponse处理，封装到AlbumSearchResponseVo
     * @param response
     * @return
     */
    private AlbumSearchResponseVo
    parseSearchResult(SearchResponse<AlbumInfoIndex> response) {
        //创建vo对象
        AlbumSearchResponseVo searchResponseVo = new AlbumSearchResponseVo();
        //获取总记录数
        HitsMetadata<AlbumInfoIndex> hits = response.hits();
        long totalValue = hits.total().value();
        //设置到vo对象里面
        searchResponseVo.setTotal(totalValue);
        //获取数据集合
        List<Hit<AlbumInfoIndex>> subHist = hits.hits();
        if (!CollectionUtils.isEmpty(subHist)) {
            List<AlbumInfoIndexVo> list = subHist.stream().map(albumInfoIndexHit -> {
                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                //获取返回具体值部分
                AlbumInfoIndex albumInfoIndex = albumInfoIndexHit.source();
                BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
                //  判断用户是否根据关键词进行检索.
                if (null != albumInfoIndexHit.highlight().get("albumTitle")) {
                    //  获取高亮数据
                    String albumTitle = albumInfoIndexHit
                            .highlight().get("albumTitle").get(0);
                    //  赋值高亮数据
                    albumInfoIndexVo.setAlbumTitle(albumTitle);
                }
                return albumInfoIndexVo;
            }).collect(Collectors.toList());

            //设置数据到vo里面  List<AlbumInfoIndexVo>
            searchResponseVo.setList(list);
        }
        return searchResponseVo;
    }

    /**
     * 根据专辑id实现上架（单线程，串行版本）
     * @param albumId
     */
    //@Override
    public void upperAlbum_alone(Long albumId) {
        //步骤：1、调用5个远程调用的方法，获取返回数据
        //根据专辑id获取专辑信息
        Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
        //返回值是result类型，从result中获取到具体值
        AlbumInfo albumInfo = albumInfoResult.getData();
        //断言做非空判断，相当于if(null == )的效果
        Assert.notNull(albumInfo, "专辑为空");

        //从专辑信息获取三级分类id，根据三级分类id获取一级和二级分类数据
        //获取到三级分类id
        Long category3Id = albumInfo.getCategory3Id();
        Result<BaseCategoryView> categoryViewResult = categoryFeignClient.getCategoryView(category3Id);
        BaseCategoryView baseCategoryView = categoryViewResult.getData();
        Assert.notNull(baseCategoryView, "分类为空");

        //根据专辑id获取四个统计数据
        Result<Map<String, Object>> albumInfoStatResult = albumInfoFeignClient.getAlbumInfoStat(albumId);
        Map<String, Object> map = albumInfoStatResult.getData();
        //Integer playStatNum = (Integer) map.get("playStatNum");

        //根据专辑id获取标签名称和标签值数据列表
        Result<List<AlbumAttributeValue>> albumAttributeValueResult =
                albumInfoFeignClient.findAlbumAttributeValue(albumId);
        List<AlbumAttributeValue> albumAttributeValueList = albumAttributeValueResult.getData();

        //从专辑信息获取userId，根据userId获取用户信息
        Long userId = albumInfo.getUserId();
        Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "用户信息为空");

        //2、将这些数据封装到AlbumInfoIndex实体类中
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        //封装albumInfo，将专辑信息复制到实体类中
        BeanUtils.copyProperties(albumInfo, albumInfoIndex);

        //封装baseCategoryView 一级二级三级分类
        Long category1Id = baseCategoryView.getCategory1Id();
        Long category2Id = baseCategoryView.getCategory2Id();
        albumInfoIndex.setCategory1Id(category1Id);
        albumInfoIndex.setCategory2Id(category2Id);
        albumInfoIndex.setCategory3Id(category3Id);

        //四个统计数据
        //实际场景应该这样，但是数据过少，不好测试
        //Integer playStatNum = (Integer) map.get("playStatNum");
        //albumInfoIndex.setPlayStatNum(playStatNum);

        //TODO 为了测试方便，四个统计数据随机数
        //更新统计量与得分，默认随机，方便测试
        int num1 = new Random().nextInt(1000);
        int num2 = new Random().nextInt(100);
        int num3 = new Random().nextInt(50);
        int num4 = new Random().nextInt(300);
        albumInfoIndex.setPlayStatNum(num1);
        albumInfoIndex.setSubscribeStatNum(num2);
        albumInfoIndex.setBuyStatNum(num3);
        albumInfoIndex.setCommentStatNum(num4);
        //热度计算
        double hotScore = num1 * 0.2 + num2 * 0.3 + num3 * 0.4 + num4 * 0.1;
        //设置热度排名
        albumInfoIndex.setHotScore(hotScore);

        //封装标签名称和标签值 List<AlbumAttributeValue> albumAttributeValueList
        // List<AlbumAttributeValue> --  List<AttributeValueIndex>
        //先判断非空，因为要将List形式的数据转换成List形式的数据，使用stream流的形式
        if (!CollectionUtils.isEmpty(albumAttributeValueList)) {
            List<AttributeValueIndex> attributeValueIndexList =
                    albumAttributeValueList.stream().map(albumAttributeValue -> {
                        AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                        BeanUtils.copyProperties(albumAttributeValue, attributeValueIndex);
                        return attributeValueIndex;
                    }).collect(Collectors.toList());

            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
        }
        //封装用户名称
        //赋值主播名称
        albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());

        //3、将这个对象添加到Elasticsearch中，也就是完成上架
        albumInfoIndexRepository.save(albumInfoIndex);
    }
}
